﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using CodeGen.TS.Generation.Members;

namespace CodeGen.TS.Generation.Generators
{

    public class ClassGenerator : ICodeGenerator
    {
        ICodeGenerator baseClassGenerator;

        public BaseTypeMode BaseTypeMode
        {
            get;
            set;
        }

        public string OutputFileName
        {
            get;
            set;
        }


        private Type type;
        public Type Type
        {
            get { return type; }
            set
            {
                type = value;
                if (newTypeName == null)
                    newTypeName = type.Name;
            }
        }
        private IList<TSPropertyInfo> PropertyInfos { get; set; }
        public bool UseObservable { get; set; }
        public bool UseInitialValue { get; set; }

        private string baseIntend = Properties.Settings.Default.IndentString;
        public string BaseIntend { get { return baseIntend; } set { baseIntend = value; } }

        private string newTypeName;
        public string NewTypeName
        {
            get
            {
                return newTypeName;
            }
            set
            {
                newTypeName = value;
            }
        }


        public string ModulName
        {
            get;
            set;
        }

        public int RecursionLevel
        {
            get;
            set;
        }

        /// <summary>
        /// Set this field to generate a method that is invoked to get a class from webservice data.
        /// 
        /// </summary>
        public string GeneratedWebServiceMethod { get; set; }

        public ClassGenerator() {
            this.BaseTypeMode = TS.BaseTypeMode.Exclude;
        }

        public ClassGenerator(Type tp)
        {
            this.Type = tp;
        }

        public string GenerateOutput()
        {
            string extendsImplementsString = "";
            if (baseClassGenerator != null)
            {
                extendsImplementsString = "extends " + (baseClassGenerator.ModulName==null ||baseClassGenerator.ModulName== this.ModulName ? "": baseClassGenerator.ModulName + ".") + baseClassGenerator.NewTypeName;
            }
            string returnString = BaseIntend + (ModulName == null ? "" : "export ") + "class " + NewTypeName + " " + extendsImplementsString + "{";
            string intend = BaseIntend + Properties.Settings.Default.IndentString;
            returnString += GenerateFields(this.PropertyInfos, this.UseObservable, this.UseInitialValue, intend);
            if (GeneratedWebServiceMethod != null)
            {
                bool isConstructor = GeneratedWebServiceMethod == "constructor";
                returnString += Environment.NewLine + intend + GeneratedWebServiceMethod + "(data: any){";
                string subintend = Properties.Settings.Default.IndentString + intend;
                if (baseClassGenerator != null && isConstructor)//Has a base class
                {
                    if (baseClassGenerator is ClassGenerator && ((ClassGenerator)baseClassGenerator).GeneratedWebServiceMethod == "constructor")
                    {
                        returnString += Environment.NewLine + subintend + "super(data);";
                    }
                    else
                    {
                        returnString += Environment.NewLine + subintend + "super();";//We assume theree is a default constructor
                    }
                }
                foreach (var property in this.PropertyInfos)
                {
                    string propertyValue = "data." + property.MemberName;

                    if (property.ScriptType == IntegratedTypescriptType.Date)
                    {
                        returnString += Environment.NewLine + subintend + "if(typeof " + propertyValue + " == \"string\")";
                        returnString += Environment.NewLine + subintend + Properties.Settings.Default.IndentString + propertyValue + " = DateUtils.getFromMSString(<string><any>" + propertyValue + ")";
                    }

                    if (isConstructor && this.UseObservable)
                    {


                        returnString += Environment.NewLine + subintend + "this." + property.MemberName + " = ko.observable(" + propertyValue + ");";
                    }
                    else if (this.UseObservable)
                    {
                        returnString += Environment.NewLine + subintend + "this." + property.MemberName + "(" + propertyValue + ");";
                    }
                    else
                    {
                        returnString += Environment.NewLine + subintend + "this." + property.MemberName + " = " + propertyValue + ";";
                    }
                }
                returnString += "}";
            }

            returnString += Environment.NewLine + BaseIntend + "}";
            return returnString;
        }

        
        string GenerateFields(IEnumerable<TSPropertyInfo> properties, bool useObservable, bool useInitialValue, string intend)
        {
            string returnString = "";
            foreach (var property in properties)
            {
                
                returnString += Environment.NewLine + intend + property.GetDefinition(useObservable);
            }
            return returnString;
        }




        private bool isPreparedForOutput = false;

        public bool IsPreparedForOutput
        {
            get { return isPreparedForOutput; }
        }

        public void PrepareForOutput(bool allowRecursion)
        {
            this.PropertyInfos = CodeGenerationHelper.GetTSFields(this, allowRecursion);
            if (BaseTypeMode == TS.BaseTypeMode.Exclude && type.BaseType!=null && type.BaseType!= typeof(object) && allowRecursion)
            {
                this.baseClassGenerator = Output.TypeGeneratorFactory.GetGenerator(type.BaseType, this.UseObservable);
                if (this.baseClassGenerator == null)
                {
                    this.baseClassGenerator = new ClassGenerator(type.BaseType);
                    this.CopyToDependend(this.baseClassGenerator);
                    if (this.type.Namespace == this.type.BaseType.Namespace)
                    {
                        this.baseClassGenerator.ModulName = this.ModulName;
                    }
                    else
                    {
                        this.baseClassGenerator.ModulName = this.type.BaseType.Namespace;
                    }
                    Output.TypeGeneratorFactory.RegisterGenerator(this.baseClassGenerator);
                }

            }
            isPreparedForOutput = true;
        }




        
    }
}
